from ppadb.client import Client as AdbClient
from ppadb.device import Device as AdbDevice
from PySide6.QtCore import QObject, QTimer, Slot, Signal, Property, QEnum, Qt
from PySide6.QtQml import QmlElement, QmlSingleton
import logging
import threading
import re
import json as JSON
import random
import struct
from .constants import *

class AdbWorker(QObject):
    
    connected = Signal()
    disconnected = Signal()
    
    updateInstallFinished = Signal(bool, str)
    getInstallVersionFinished = Signal(bool, str)
    getBlePairingCodeFinished = Signal(bool, int)
    setBlePairingCodeFinished = Signal(bool, str)
    killRosFinished = Signal(bool, str)
    setSerialNumberFinished = Signal(bool, int)
    getSerialNumberFinished = Signal(bool, int)
    
    def __init__(self, parent=None):
        super().__init__(parent)
        self._client:AdbClient = None
    
    def generate_pairing_code(device_id=""):
        """生成pairing code"""

        #生成新的配对码
        code = random.randint(100000, 999999)
        return code
    
    def get_device(self)->AdbDevice:
        try:
            if self._client is None:
                logging.error("ADB client not connected")
                return None
            devices = self._client.devices()
            if not devices:
                logging.error("No ADB devices connected")
                return None
            return devices[0]  # 假设只连接一个设备
        except Exception as e:
            logging.error(f"Error getting device: {e}")
            return None
             
    @Slot()
    def connectDev(self):
        try:
            self._client = AdbClient(host="127.0.0.1", port=5037)   
            logging.debug("ADB client connected")   
            self.connected.emit()      
        except Exception as e:
            logging.error(f"ADB connection error: {e}")
            self.disconnected.emit()
            return
    
    @Slot()
    def disconnect(self):
        """断开连接"""
        if self._client:
            # what to do?
            self.disconnected.emit()
    
    @Slot(str)
    def update_install(self, local_path):
        try:
            # self._device.shell('cat /userdata/install/version')
            logging.info(f"update_install called in thread {threading.get_ident()}")
            if not self._device or not self._device.available:
                error_msg = "Device not connected"
                self.updateInstallFinished.emit(False, error_msg)
                return
            self._device.push(local_path, '/userdata/install/update.tar.gz', progress_callback= lambda x, y: logging.info(f"Progress: {x}/{y} bytes"))
            self._device.shell('cd /userdata/ && rm -r install && tar -xzf update.tar.gz && rm update.tar.gz && sync') 
            self.updateInstallFinished.emit(True, "Update successful")
        except Exception as e:
            logging.error(f"Update failed: {e}")
            self.updateInstallFinished.emit(False, str(e))
    
    @Slot()
    def get_install_version(self):
        # try:
        #     if not self._device or not self._device.available:
        #         error_msg = "Device not connected"
        #         self.getInstallVersionFinished.emit(False, error_msg)
        #         return
        #     version = self._device.shell(f'cat {SOFTWARE_VERSION_FILE_PATH}')
        #     self.getInstallVersionFinished.emit(True, version)
        # except Exception as e:
        #     logging.error(f"Get install version failed: {e}")
        #     self.getInstallVersionFinished.emit(False, str(e))
        device = self.get_device()
        if device is None: 
            self.getInstallVersionFinished.emit(False, "Device not connected")
            return
        try:
            version = device.shell(f'cat {SOFTWARE_VERSION_FILE_PATH}')
            self.getInstallVersionFinished.emit(True, version)
        except Exception as e:
            logging.error(f"Get install version failed: {e}")
            self.getInstallVersionFinished.emit(False, "Error getting install version")
    
    @Slot()
    def get_ble_pairing_code(self):
        device = self.get_device()
        if device is None:
            self.getBlePairingCodeFinished.emit(False, -1)
            return
        try:
            code:str = device.shell(f'od -An -t u4 -w4 -v {PAIRING_CODE_FILE_PATH}')
            code.strip()
            self.getBlePairingCodeFinished.emit(True, int(code))
            logging.info(f"BLE pairing code: {int(code)}")
        except Exception as e:
            logging.error(f"Get BLE pairing code failed: {e}")
            self.getBlePairingCodeFinished.emit(False, -1)
    
    @Slot(str)
    def set_ble_pairing_code(self, code):
        cmd = f"python -c \"import struct; open('{PAIRING_CODE_FILE_PATH}', 'wb').write(struct.pack('<I', {code}))\""
        device = self.get_device()
        if device is None:
            self.setBlePairingCodeFinished.emit(False, "Device not connected")
            return
        try:
            device.shell(cmd)
            device.shell('sync')
            self.setBlePairingCodeFinished.emit(True, "BLE pairing code set successfully")
        except Exception as e:
            logging.error(f"Set BLE pairing code failed: {e}")
    
    @Slot()
    def kill_ros(self):
        pass
    
    @Slot(int)
    def set_serial_number(self, serial_number: int):
        device = self.get_device()
        if device is None:
            self.getSerialNumberFinished.emit(False, -1)
            return
        json_txt = device.shell(f"cat {PCBA_FILE_PATH}")
        info = JSON.loads(json_txt)
        info["serial_number"] = serial_number
        # 写回文件
        device.shell(f"echo '{JSON.dumps(info)}' > {PCBA_FILE_PATH}")
        self.setSerialNumberFinished.emit(True, serial_number)
    
    @Slot()
    def get_serial_number(self)->str:
        logging.debug("get serial number called")
        device = self.get_device()
        if device is None:
            logging.error("Device not connected")
            self.getSerialNumberFinished.emit(False, -1)
            return
        try:
            json_txt = device.shell(f"cat {PCBA_FILE_PATH}")
            info = JSON.loads(json_txt)
            sn = info.get("serial_number", None)
            
            if sn:
                self.getSerialNumberFinished.emit(True, sn)
            else:
                self.getSerialNumberFinished.emit(False, -1)
        except Exception as e:
            logging.error(f"Get serial number failed: {e}")
            self.getSerialNumberFinished.emit(False, -1)
    
    
            